package singleton;

class SingletonDemo{
    public int flg = 0;
    // 防止被new
    private SingletonDemo(){}
    // 在静态区初始化当前的实例 【饿汉模式】【线程安全】 JVM保证一个class只会load内存一次。 static 保证load内存初始化一次。所以也是一次。
    // 把类加载到内存Class.forName()。
    private static final SingletonDemo INS = new SingletonDemo();
    // 对外获取
    public static SingletonDemo getInstance(){
        return INS;
    }
}


class SingletonDemo2{
    public int flg = 2;

    private SingletonDemo2(){}

    private static final SingletonDemo2 INS;

    static {
        INS = new SingletonDemo2();
    }

    public static SingletonDemo2 getInstance(){
        return INS;
    }
}

/**
 * 懒汉式 线程不安全
 *
 * 改为线程安全的方式
 * 1.函数直接加
 * public synchronized static SingletonDemo3  getInstance()
 * 2.缩小范围加锁
 * synchronized (SingletonDemo3.class) {
 *
 *
 * 加锁效率会下降。
 *
 * 为什么可以反序列化出来就被实例化
 * https://blog.csdn.net/leo187/article/details/104332138
 */
class SingletonDemo3{
    public int flg = 0;

    private SingletonDemo3(){}
    // 在jit优化器优化的时候防止指令集重排，这里需要对共享属性加volatile防止重排。java 汇编语言 的优化器指令重排
    // class 就是java的汇编
    // jit优化为本地代码
    private static volatile SingletonDemo3 INS;

    public static SingletonDemo3  getInstance(){
        if(null == INS){
            // 缩小范围加锁
            synchronized (SingletonDemo3.class) {
                if(null == INS) {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INS = new SingletonDemo3();
                }
            }
        }

        return INS;
    }
}

/**
 * 静态内部类写法
 * 线程安全 饱汉模式
 *
 * 如果只加载 SingletonDemo4 GetSingletonDemo4这个是不会被初始化的
 * 只有调用 getInstance GetSingletonDemo4 才会加载到内存，加载到内存以后才会初始化静态区的内容并且创建实力。
 * jvm 保证内存class加载一次静态区的内容加载一次。
 */

class SingletonDemo4{
    private static class GetSingletonDemo4{
        private static final SingletonDemo4 INS = new SingletonDemo4();
    }

    private SingletonDemo4(){}

    public static SingletonDemo4 getInstance(){
        return GetSingletonDemo4.INS;
    }
}

// 上面的任何方式都是可以找到class load到内存然后然后通过反序列化实例出来。因为有构造方法。

/**
 * EffectJava 最牛逼的模型
 * Enum 实现
 *
 * 不能反编译并且线程安全。
 *
 * 枚举没有构造方法所以不能被反序列化出来。
 */
enum EF{
    INS;
    void sayHi(){
        System.out.println("hi");
    }
}

public class Singleton {
    public static void main(String[] args) {
        System.out.println("singleton");
        SingletonDemo singletonDemo1 = SingletonDemo.getInstance();
        SingletonDemo singletonDemo2 = SingletonDemo.getInstance();

        System.out.println(singletonDemo1.flg);
        System.out.println(singletonDemo1 == singletonDemo2);

        System.out.println("SingletonDemo2");

        SingletonDemo2 singletonDemo21 = SingletonDemo2.getInstance();
        SingletonDemo2 singletonDemo22 = SingletonDemo2.getInstance();
        System.out.println(singletonDemo21 == singletonDemo22);
        System.out.println(singletonDemo22.flg);

        // 懒汉式
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(SingletonDemo3.getInstance().hashCode());
            }).start();
        }

        System.out.println("demo4------>>>>");
        System.out.println(SingletonDemo4.getInstance() == SingletonDemo4.getInstance());

        System.out.println("demo05");
        EF.INS.sayHi();
    }
}
